﻿using System;

namespace Generator
{
    public class StochasticParameters
    {   
        public int initialTemperature;
        public int finalTemperature;

        public int swapAttemptsIn1Go;
        public int reassignAttemptsIn1Go;

        public int halfPoolSize;
        public int poolSize
        {
            get { return halfPoolSize * 2; }
        }
        public int iterations;
        
        public int[] replaceTable;
        public int[] temperatureTable;

        public int applyMutationFrom;

        /* temperatureTable - the array - always contains 100 values;
         * it returns the temperature for the given; not iterations.
         * so if there'pShift 1000 iterations for example - the temperature
         * used for first 10 of them will be temperatureTable[0] -
         * then temperatureTable[1] for the next 10, etc. */
        public void createTemperatureTable()
        {
            temperatureTable = new int[100];

            for (int i = 0; i < 100; i++)
            {
                temperatureTable[i] = finalTemperature - ((finalTemperature - initialTemperature)*(100-i) / 100);
            }
        }

        //public void createReplaceTable(int whatPartSurvives)
        //{
        //    replaceTable = new int[poolSize];
        //    for (var i = 0; i < poolSize; i++)
        //    {
        //        replaceTable[i] = (i % (poolSize/whatPartSurvives));
        //    }
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param Name="iterations"></param>
        /// <param Name="whatPartSurvives">2 = a half (the better one) survives. 3 = one third. etc.</param>
        public void createReplaceTableSTEEP(int whatPartSurvives)
        {
            replaceTable = new int[poolSize];

            if (whatPartSurvives < 1)
            {
                throw new ArgumentException("createReplaceTableSteep::whatPartSurvives can't be smaller than 1");
            }
            if (whatPartSurvives > poolSize)
            {
                throw new ArgumentException("createReplaceTableSteep::whatPartSurvives can't be bigger than poolSize");
            }

            int survivingBunch = poolSize / whatPartSurvives;
            // it'pShift rounded down. eg. if geneticPool size = 100, and survives every third solution, there will be 33 survivors not 34.

            // if there'pShift 16 survivors, the best one - #0 - will be preserved 11 times, worst one #15 - only once
            var howManyTimesReproduced = new int[survivingBunch];
            
            /* every solution can be multiplied "whatPartSurvives" times, which makes great sense,
             * because - for example - if you preserve one third of the geneticPool,
             * then obviously every "elite" solution can be copied three times. */

            var championReproductionTimes = (2 * whatPartSurvives) - 1;
            
            for (int i = 0; i < survivingBunch; i++)
            {
                howManyTimesReproduced[i] = (((survivingBunch - 1 - i) * championReproductionTimes) / survivingBunch) + 1;
            }
            int result=0;
            for(int i=0;i < howManyTimesReproduced.Length;i++)
                result += howManyTimesReproduced[i];
            
            for (int i = 0; i < (poolSize - result); i++)
            {
                howManyTimesReproduced[i % howManyTimesReproduced.Length]++;
            }

            int k = 0;
            for (int i = 0; i < howManyTimesReproduced.Length; i++)
            {
                for (int j = 0; j < howManyTimesReproduced[i]; j++)
                {
                    replaceTable[k] = i;
                    k++;
                }
            }
        }

        public void setTemperature(int initial, int final)
        {
            initialTemperature = initial;
            finalTemperature = final;
        }

        public StochasticParameters(
            bool retrieveFromSettings,
            SpeedMode pSpeedMode)
        {
            if (retrieveFromSettings)
            {
                RetrieveParametersFromSettings(pSpeedMode);
            }
        }

        public StochasticParameters(
            int halfPool,
            int iterations,
            int initialTemp,
            int finalTemp,
            int swapsIn1Go,
            int reassignsIn1Go,
            int mutationBar)
            : this(false, SpeedMode.None)
        {
            halfPoolSize = halfPool;
            this.iterations = iterations;
            swapAttemptsIn1Go = swapsIn1Go;
            reassignAttemptsIn1Go = reassignsIn1Go;
            applyMutationFrom = mutationBar;
            setTemperature(initialTemp, finalTemp);
        }

        public void RetrieveParametersFromSettings(SpeedMode pSpeedMode)
        {
            switch (pSpeedMode)
            {
                case SpeedMode.Slow:
                    {
                        halfPoolSize = Settings.Default.STOCHASTIC_HalfPoolSize_SLOW;
                        iterations = Settings.Default.STOCHASTIC_Iterations_SLOW;
                        break;
                    }
                case SpeedMode.Express:
                    {
                        halfPoolSize = Settings.Default.STOCHASTIC_HalfPoolSize_EXPRESS;
                        iterations = Settings.Default.STOCHASTIC_Iterations_EXPRESS;
                        break;
                    }
                case SpeedMode.Fast:
                default:
                    {
                        halfPoolSize = Settings.Default.STOCHASTIC_HalfPoolSize_FAST;
                        iterations = Settings.Default.STOCHASTIC_Iterations_FAST;
                        break;
                    }                    
                    
            }
            

            swapAttemptsIn1Go = Settings.Default.STOCHASTIC_SwappingAttempts;
            reassignAttemptsIn1Go = Settings.Default.STOCHASTIC_ReassigningAttempts;

            applyMutationFrom = Settings.Default.STOCHASTIC_ApplyMutationFrom;

            setTemperature(
                Settings.Default.STOCHASTIC_InitialTemperature,
                Settings.Default.STOCHASTIC_FinalTemperature);
            createTemperatureTable();

            createReplaceTableSTEEP(Settings.Default.STOCHASTIC_SurvivalRate);

        }
    }
}
